package uk.ac.aber.paws.client.system;

import java.awt.Dialog;

import java.awt.Frame;

import java.awt.TextField;

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import org.xvolks.jnative.JNative;

import org.xvolks.jnative.exceptions.NativeException;

import org.xvolks.jnative.misc.MSG;

import org.xvolks.jnative.misc.basicStructures.HWND;

import org.xvolks.jnative.misc.basicStructures.LPARAM;

import org.xvolks.jnative.misc.basicStructures.LRESULT;

import org.xvolks.jnative.misc.basicStructures.UINT;

import org.xvolks.jnative.misc.basicStructures.WPARAM;

import org.xvolks.jnative.util.Callback;

import org.xvolks.jnative.util.Kernel32;

import org.xvolks.jnative.util.User32;

import org.xvolks.jnative.util.WindowProc;

import org.xvolks.jnative.util.constants.winuser.WM;

import org.xvolks.jnative.util.constants.winuser.WindowsConstants;


import com.sun.jna.platform.win32.WinDef.*;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 16th August 2011
 * 
 * Win32SessionManager.java
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

public class Win32SessionManager

{

     

    public interface EndSessionListener

    {

        public enum Origin

        {

            CONSOLE,

            WINDOW,

            ;

        }

        /***

         * Called when a EndSession event is detected

         * @param org

         * @return true to let the session to close, false to stop that process

         */

        public boolean queryEndSessionOccured(Origin org);

         

        /***

         * This event is only avaliable if launched with javaw.exe (windows)

         * @param org

         */

        public void endSessionOccured(Origin org);

         

        /***

         * This event is only avaliable if launched with java.exe (console)

         * @param org

         */

        public void shutdownOccured(Origin org);

    }

     

    private static final CallBackWindowProc console_proc = new CallBackWindowProc();

     

    private static List<EndSessionListener> listeners = new ArrayList<EndSessionListener>();

     

     

    public static void addEndSessionListener(EndSessionListener listener)

    {

        if(!listeners.contains(listener))
        {

            listeners.add(listener);

        }

    }

    public static boolean removeEndSessionListener(EndSessionListener listener)

    {

        return listeners.remove(listener);

    }

     

    public static boolean fireQueryEndSessionOccured(EndSessionListener.Origin origin)

    {

        boolean ret = true;

        for(EndSessionListener l : listeners)

        {

            ret &= l.queryEndSessionOccured(origin);

        }

        return ret;

    }

     

    public static void fireEndSessionOccured(EndSessionListener.Origin origin)

    {

        for(EndSessionListener l : listeners)

        {

            l.endSessionOccured(origin);

        }

    }

     

    public static void fireShutdownOccured(EndSessionListener.Origin origin)

    {

        for(EndSessionListener l : listeners)

        {

            l.shutdownOccured(origin);

        }

    }

     

    static class CallBackWindowProc implements Callback

    {

        public int HandlerRoutine(int msg)

        {

            switch (msg)

            {

              

                case 5:

                    return fireQueryEndSessionOccured(EndSessionListener.Origin.CONSOLE) ? 0 : 1;

                case 6:

                    fireShutdownOccured(EndSessionListener.Origin.CONSOLE);

                    break;

                     

                default:

            }

            return 1;

             

        }

         

        public int callback(long[] values)

        {

            return HandlerRoutine(

                    (int) values[0]);

        }

         

        private int instance=0;

        public int getCallbackAddress() throws NativeException

        {

            if (instance==0)

            {

                instance=JNative.createCallback(1, this);

            }

            return instance;

             

        }

        public boolean releaseCallbackAddress() throws NativeException

        {

            if(instance != 0 && JNative.releaseCallback(this))

            {

                instance = 0;

                return true;

            }

            else

            {

                return false;

            }

             

        }

    }

     

     

    private static HWND _hwnd   = null;

    private static int oldWindowProc;

     

    /***

     *

     * @throws NativeException

     * @throws IllegalAccessException

     */

    public static boolean unregisterEndSessionHook() throws NativeException, IllegalAccessException

    {

        boolean ret = Kernel32.SetConsoleCtrlHandler(console_proc, false);

        if(JNative.isLogginEnabled())

            JNative.getLogger().log("RemoveConsoleHandler : " + ret);

        if(ret)

        {

            console_proc.releaseCallbackAddress();

        }
         

        if(oldWindowProc != 0)

        {

            LRESULT result = User32.SendMessage(_hwnd, new UINT(WM.WM_CLOSE.getValue()), new WPARAM(0), new LPARAM(0));

            if(JNative.isLogginEnabled())

            {

                JNative.getLogger().log("SendMessage WM_CLOSE returned " + result.getValue());

            }

            ret &= result.getValue() == 0;

        }

         

        return ret;

    }

 

    public static void registerEndSessionHook(final String windowName) throws NativeException, IllegalAccessException

    {

        if(JNative.isLogginEnabled())

            JNative.getLogger().log("SetConsoleHandler : " + Kernel32.SetConsoleCtrlHandler(console_proc, true));

        else

            Kernel32.SetConsoleCtrlHandler(console_proc, true);

         

        new Thread()

        {

            {

                setDaemon(true);

                setName("EndSessionListener");

            }

            public void run()

            {

                 

                WindowProc proc = new WindowProc()

                {

                    public int windowProc(int hwnd, int uMsg, int wParam, int lParam)

                    {

                        writeLog("log "+uMsg);

                        if (_hwnd != null || hwnd == _hwnd.getValue())

                        {

                             

                            if(uMsg == WM.WM_QUERYENDSESSION.getValue())

                            {

                                return fireQueryEndSessionOccured(EndSessionListener.Origin.WINDOW) ? -1 : 0;

                            }

                            else if(uMsg == WM.WM_ENDSESSION.getValue())

                            {

                                fireEndSessionOccured(EndSessionListener.Origin.WINDOW);

                                return -1;

                            }

                            else

                            {

                                try

                                {

                                    int ret = User32.DefWindowProc(new HWND(hwnd), new UINT(uMsg), new WPARAM(wParam),

                                            new LPARAM(lParam)).getValue();

                                    return ret;

                                }

                                catch (Exception ex)

                                {

                                    return 0;

                                }

                            }

                        }

                        else

                        {

                            JNative.getLogger().log("Je ne gere pas a !");

                             

                            return -1;

                        }

                    }
                     

                };

                 

                 

                try

                {

                    // Pointer p = new

                    // Pointer(MemoryBlockFactory.createMemoryBlock(20));

                    // p.setStringAt(0, "Invisible");

                    _hwnd = new HWND(User32.CreateWindowEx(0,

                            "Message", windowName==null?"":windowName, 0, 0, 0, 100, 100, 0, 0, JNative.getCurrentModule(), 0));

                    if(0==_hwnd.getValue())

                    {

                        JNative.getLogger().log("Failed to create the message listener window");

                    }

                }

                catch (Exception ex)

                {

                    ex.printStackTrace();

                    JNative.getLogger().log("Can't create the message listener window.");

                }

                try

                {

                    oldWindowProc = JNative.registerWindowProc(_hwnd, proc);

                    // User32.SetWindowLong(_hwnd, WindowsConstants.GWL_WNDPROC, new

                    // LONG(test));

                }

                catch (Exception e1)

                {

                    e1.printStackTrace();

                }

                 

                 

                try

                {

                    User32.ShowWindow(_hwnd, WindowsConstants.SW_HIDE);

                    User32.UpdateWindow(_hwnd);

                }

                catch (Exception e)

                {

                    e.printStackTrace();

                }

                 

                 

                MSG msg = null;

                try

                {

                    msg = new MSG();

                }

                catch (NativeException e)

                {

                    e.printStackTrace();

                }

                 

                 

                boolean lQuit = false;

                try

                {

                    while (!lQuit)

                    {

                         

                        switch (User32.GetMessage(msg, new HWND(0), 0, 0))

                        {

                            case -1:

                                int error = Kernel32.GetLastError();

                                JNative.getLogger().log("Error occured: " + error);

                                lQuit = true;

                                break;

                            case 0:

                                JNative.getLogger().log("WM_QUIT received");

                                lQuit = true;

                                break;

                        }

                         

                        User32.TranslateMessage(msg);

                        User32.DispatchMessage(msg);

                    }

                }

                catch (Exception ex)

                {

                    ex.printStackTrace();

                }

            }

        }.start();

         

    }

     

    private static PrintWriter log=null;

    private static FileOutputStream fLog=null;

     

    public static void writeLog(String message)

    {

        if (fLog==null)

        {

            try

            {

                fLog=new FileOutputStream("./Win32SessionManager.log");

                log=new PrintWriter(fLog);

                Runtime.getRuntime().addShutdownHook(new Thread()

                {

                    public void run()

                    {

                        writeLog("Closing log");

                        log.close();

                    }

                });

            }

            catch (Exception e)

            {

                return;

            }

        }

         

        log.println("["+new Date()+"] "+message);

        log.flush();

        try

        {

            fLog.getChannel().force(false);

        }

        catch (IOException e)

        {

        }

    }

     

    public static void main(String[] args) throws NativeException, IllegalAccessException

    {

         

        writeLog("Running");

        addEndSessionListener(new EndSessionListener()

        {

            public void endSessionOccured(Origin org)

            {

                writeLog("Fin de session par "+org);

                 

            }

             

            public boolean queryEndSessionOccured(Origin org)

            {

                writeLog("Demande de fin de session par "+org);

                return true;

            }

             

            public void shutdownOccured(Origin org)

            {

                writeLog("Shutdown captured from "+org);

            }

             

        });

         

        writeLog("Registring hook");

        registerEndSessionHook("Noone");

        final Dialog d = new Dialog(new Frame());

         

        d.setModal(true);

        d.add(new TextField("Testing, please close session"));

        d.pack();

        d.addWindowListener(new WindowAdapter()

        {

             

            /** (non-Javadoc)

             * @see java.awt.event.WindowAdapter#windowClosing(java.awt.event.WindowEvent)

             */

            @Override

            public void windowClosing(WindowEvent e)

            {

                d.dispose();

            }

             

        });

        d.setVisible(true);

         

        writeLog("Exiting");

        System.exit(0);

         

    }

}
